package service

import (
	"context"
	"errors"
	"fmt"
	"gitee.com/xiao_hange/go-admin-file/file/domain"
	events "gitee.com/xiao_hange/go-admin-file/file/events/file"
	"gitee.com/xiao_hange/go-admin-file/file/repository"
	"gitee.com/xiao_hange/go-admin-file/file/service/file"
	"gitee.com/xiao_hange/go-admin-pkg/pkg/logger"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/trace"
	"math/rand"
	"strconv"
	"strings"
	"time"
)

type FileService interface {
	UploadFile(ctx context.Context, req domain.UploadFlieReq) (domain.UploadFlie, error)
	DelFile(ctx context.Context, id int64, name string) error

	GetLifeLst(ctx context.Context, page, limit int, types uint8) ([]domain.File, int, error)
	GetExportData(ctx context.Context, page, limit, export int, mail string) ([]domain.Data, int, error)
	EditName(ctx context.Context, id int64, name string) error
}

var (
	ErrDataTooMuch = repository.ErrDataTooMuch
)

type fileService struct {
	fileSvc  file.Service
	repo     repository.FileRepository
	Producer events.Producer
	l        logger.AppLogger
	tracer   trace.Tracer
}

func NewFileService(fileSvc file.Service, repo repository.FileRepository, producer events.Producer, l logger.AppLogger) FileService {
	return &fileService{
		fileSvc:  fileSvc,
		repo:     repo,
		Producer: producer,
		l:        l,
		tracer:   otel.Tracer("File服务-Server层"),
	}
}

func (f *fileService) UploadFile(ctx context.Context, req domain.UploadFlieReq) (domain.UploadFlie, error) {
	var (
		types  = 1
		suffix = "png"
	)
	if strings.HasPrefix(req.FileType, "video/") {
		types = 2
		suffix = "mp4"
	}
	var (
		key = f.generateUniqueFileName()
		val = fmt.Sprintf("go-admin/%d.%s", key, suffix)
		url = fmt.Sprintf("http://cdn.jdc51.com/%s", val)
	)
	err := f.fileSvc.UploadImage(ctx, val, req.Reader)
	if err != nil {
		return domain.UploadFlie{}, err
	}

	err = f.repo.Add(ctx, domain.File{
		OldName:  req.OldName,
		NewName:  strconv.FormatUint(key, 10),
		FileType: req.FileType,
		Size:     req.Size,
		Image:    val,
		Type:     uint8(types),
	})

	if err != nil {
		return domain.UploadFlie{}, err
	}

	return domain.UploadFlie{
		Val: val,
		Url: url,
	}, nil
}

func (f *fileService) GetLifeLst(ctx context.Context, page, limit int, types uint8) ([]domain.File, int, error) {
	lsits, total, err := f.repo.List(ctx, page, limit, types)
	return lsits, total, err
}

func (f *fileService) EditName(ctx context.Context, id int64, name string) error {
	return f.repo.UpdateNameById(ctx, id, name)
}

func (f *fileService) DelFile(ctx context.Context, id int64, name string) error {
	var err error
	key := fmt.Sprintf("go-admin/%s.png", name)
	if err = f.fileSvc.DeleteImage(ctx, key); err != nil {
		return err
	}
	return f.repo.Delete(ctx, id)

}

func (f *fileService) generateUniqueFileName() uint64 {
	rand.Seed(time.Now().UnixNano())
	return uint64(rand.Uint32()) | (uint64(rand.Uint32()) << 32)
}

func (f *fileService) GetExportData(ctx context.Context, page, limit, export int, mail string) ([]domain.Data, int, error) {
	spanCtx, span := f.tracer.Start(ctx, "File服务-Server层: GetExportData")
	defer span.End()
	lsits, total, err := f.repo.Export(spanCtx, page, limit, export)
	if errors.Is(err, ErrDataTooMuch) && export == 1 && total > 1000 && mail != "" {
		//TODO 要是导出的数据需要查询条件 在 ReadEvent 里面填上对应的条件即可 该发放为演示没有添加过滤条件
		err = f.Producer.ProducerReadEvent(spanCtx, events.ReadEvent{
			Mail: mail,
		})
		if err != nil {
			f.l.Error("写入队列失败", logger.Error(err))
			return nil, 0, err
		}
		f.l.Info("写入队列成功")
		return nil, 0, ErrDataTooMuch
	}

	return lsits, total, err
}
